using System;

using AGG.Transform;
using AGG.Image;
using AGG.VertexSource;
using AGG.RasterizerScanline;
using AGG.UI;

namespace AGG
{
    public class image1_application : MatchParentBoundsWidget
    {
        AGG.UI.SliderWidget drawAngle;
        AGG.UI.SliderWidget drawScale;
        public static ImageBuffer sourceImage = new ImageBuffer();

        Vector2D orignialSize;

        image1_application()
        {
            drawAngle = new AGG.UI.SliderWidget(5, 5, 300, 12);
            drawScale = new AGG.UI.SliderWidget(5,  5+15, 300, 12+15);
            drawAngle.ValueChanged += new AGG.UI.SliderWidget.ValueChangedEventHandler(NeedsRedraw);
            drawScale.ValueChanged += new AGG.UI.SliderWidget.ValueChangedEventHandler(NeedsRedraw);

            AddChild(drawAngle);
            AddChild(drawScale);
            drawAngle.label("Angle={0:F2}");
            drawScale.label("Scale={0:F2}");
            drawAngle.range(-180.0, 180.0);
            drawAngle.value(0.0);
            drawScale.range(0.1, 5.0);
            drawScale.value(1.0);
        }

        void NeedsRedraw(object sender, EventArgs e)
        {
            Invalidate();
        }

#if use_timers
        static CNamedTimer AllTimer = new CNamedTimer("All");
        static CNamedTimer image1_100_Times = new CNamedTimer("image1_100_Times");
#endif         
        public override void OnDraw(Graphics2D graphics2D)
        {
            if (orignialSize.x == 0)
            {
                orignialSize.x = Width;
                orignialSize.y = Height;
            }

            ImageBuffer destImageWithPreMultBlender = new ImageBuffer();
            switch (graphics2D.DestImage.BitDepth)
            {
                case 24:
                    destImageWithPreMultBlender.Attach(graphics2D.DestImage, new BlenderPreMultBGR());
                    break;

                case 32:
                    destImageWithPreMultBlender.Attach(graphics2D.DestImage, new BlenderPreMultBGRA());
                    break;

                default:
                    throw new Exception("Unknown bit depth");
            }

            ImageClippingProxy clippingProxy_pre = new ImageClippingProxy(destImageWithPreMultBlender);

            clippingProxy_pre.clear(new RGBA_Doubles(1.0, 1.0, 1.0));

            Affine src_mtx = Affine.NewIdentity();
            src_mtx *= Affine.NewTranslation(-orignialSize.x / 2 - 10, -orignialSize.y / 2 - 20 - 10);
            src_mtx *= Affine.NewRotation(drawAngle.value() * Math.PI / 180.0);
            src_mtx *= Affine.NewScaling(drawScale.value());
            src_mtx *= Affine.NewTranslation(orignialSize.x / 2, orignialSize.y / 2 + 20);

            Affine img_mtx = Affine.NewIdentity();
            img_mtx *= Affine.NewTranslation(-orignialSize.x / 2 + 10, -orignialSize.y / 2 + 20 + 10);
            img_mtx *= Affine.NewRotation(drawAngle.value() * Math.PI / 180.0);
            img_mtx *= Affine.NewScaling(drawScale.value());
            img_mtx *= Affine.NewTranslation(orignialSize.x / 2, orignialSize.y / 2 + 20);
            img_mtx.invert();

            AGG.span_allocator sa = new span_allocator();

            span_interpolator_linear interpolator = new span_interpolator_linear(img_mtx);

            span_image_filter sg;
            switch(sourceImage.BitDepth)
            {
                case 24:
                    {
                        ImageBufferAccessorClip source = new ImageBufferAccessorClip(sourceImage, RGBA_Doubles.rgba_pre(0, 0, 0, 0).GetAsRGBA_Bytes());
                        sg = new span_image_filter_rgb_bilinear_clip(source, RGBA_Doubles.rgba_pre(0, 0.4, 0, 0.5), interpolator);
                    }
                    break;

                case 32:
                    {
                        ImageBufferAccessorClip source = new ImageBufferAccessorClip(sourceImage, RGBA_Doubles.rgba_pre(0, 0, 0, 0).GetAsRGBA_Bytes());
                        sg = new span_image_filter_rgba_bilinear_clip(source, RGBA_Doubles.rgba_pre(0, 0.4, 0, 0.5), interpolator);
                    }
                    break;

                default:
                    throw new Exception("Bad sourc depth");
            }

            ScanlineRasterizer ras = new ScanlineRasterizer();
            ras.SetVectorClipBox(0, 0, Width, Height);
            ScanlineCachePacked8 sl = new ScanlineCachePacked8();
            //scanline_unpacked_8 sl = new scanline_unpacked_8();

            double r = orignialSize.x;
            if (orignialSize.y - 60 < r)
            {
                r = orignialSize.y - 60;
            }

            VertexSource.Ellipse ell = new VertexSource.Ellipse(orignialSize.x / 2.0 + 10,
                orignialSize.y / 2.0 + 20 + 10, 
                r / 2.0 + 16.0, 
                r / 2.0 + 16.0, 200);
            
            VertexSourceApplyTransform tr = new VertexSourceApplyTransform(ell, src_mtx);

            ras.add_path(tr);
#if use_timers
            for (int j = 0; j < 10; j++)
            {
                ScanlineGraphics2D.GenerateAndRender(ras, sl, clippingProxy_pre, sa, sg);
            }
            AllTimer.Start();
            image1_100_Times.Start();
            for(int i=0; i<500; i++) 
            {
#endif
            //clippingProxy_pre.SetClippingBox(30, 0, (int)width(), (int)height());
            agg_renderer_scanline.Default.GenerateAndRender(ras, sl, clippingProxy_pre, sa, sg); 
#if use_timers
            }
            image1_100_Times.Stop();
#endif

#if use_timers
            AllTimer.Stop();
            CExecutionTimer.Instance.AppendResultsToFile("TimingTest.txt", AllTimer.GetTotalSeconds());
            CExecutionTimer.Instance.Reset();
#endif
            base.OnDraw(graphics2D);
        }

/*
E:\agg23\examples\image1.cpp(111) : error C2664: 

  '__thiscall agg::span_image_filter_gray_bilinear<struct agg::gray8,
                                                   struct agg::order_bgra,
                                                   class agg::span_interpolator_linear<class agg::trans_affine,8> >::agg::span_image_filter_gray_bilinear<struct agg::gray8,struct agg::order_bgra,class agg::span_interpolator_linear<class agg::trans_affine,8> >(class agg::span_interpolator_linear<class agg::trans_affine,8> &,const class agg::row_ptr_cache<unsigned char> &,const struct agg::gray8 &,struct agg::order_bgra &)' : 

cannot convert parameter 1 from 

'class agg::span_allocator<struct agg::gray8>' to 
'class agg::span_interpolator_linear<class agg::trans_affine,8> &'
*/
		public static void StartDemo()
		{
            GuiHalFactory.SetGuiBackend(GuiHalFactory.KnownGuiFactoriesIndexes.WindowsFormsBitmap);

            string img_name = "spheres.bmp";
            if (!ImageBMPIO.LoadImageData(img_name, image1_application.sourceImage))
            {
                string buf;
                buf = "File not found: "
                    + img_name
                    + ".bmp"
                    + ". Download http://www.antigrain.com/" + img_name + ".bmp" + "\n"
                    + "or copy it from another directory if available.";
                GuiHalSurface primaryWindow = GuiHalFactory.CreatePrimarySurface(10, 10, GuiHalSurface.CreateFlags.Resizable, GuiHalSurface.PixelFormat.PixelFormatBgr24);
                primaryWindow.ShowSystemMessage(buf);
            }
            else
            {
                int x = image1_application.sourceImage.Width + 20;
                int y = image1_application.sourceImage.Height + 40 + 20;
                GuiHalSurface primaryWindow = GuiHalFactory.CreatePrimarySurface(x, y, GuiHalSurface.CreateFlags.Resizable, GuiHalSurface.PixelFormat.PixelFormatBgr24);
                //GuiHalSurface primaryWindow = GuiHalFactory.CreatePrimarySurface(x, y, GuiHalSurface.CreateFlags.Resizable, GuiHalSurface.PixelFormat.PixelFormatBgra32);

                primaryWindow.Caption = "Image Affine Transformations with filtering";

                primaryWindow.AddChild(new image1_application());
                primaryWindow.Run();
            }
		}

		[STAThread]
        public static void Main(string[] args)
        {
        	StartDemo();
        }
    };
}
